/* I Gotta Feeling.... */

/* gaeQuery is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of 
 * the License, or (at your option) any later version.
 *
 * gaeQuery is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with gaeQuery. If not, see 
 * <http://www.gnu.org/licenses/>.
 */
 
(function (window, undefined) {

	function deserializeObject(obj) {
		//console.log("OBJ:", obj);
		var key = new GaeKey(obj.__key.__kind, obj.__key.__name, obj.__key.__repr);
		var entity = new GaeEntity(key.kind());
		entity = $.extend({}, obj, entity);
		entity.key(key);
		//console.log("BEfore ReturNing:", entity);
		return entity;
	}


	function serializeObject(oldObj) {
		/* Ignore all keys starting with an _(underscore) */
		var obj = $.extend({}, oldObj);
		for (key in obj) {
			if (key.length > 0 && key[0] == "_") {
				delete obj[key];
			}
		}
		obj.__key = oldObj.key();
		return JSON.stringify(obj);
	}

	function serializeObjectKey(obj) {
		return JSON.stringify(obj.key());
	}

	function gaeGetRequest(params) {
		var defaultParams = {
			baseURL: null,
			model: null,
			filters: [],
			order: "",
			limit: 1,
			offset: 0,
			success: null, 
			error: null
		};
		var params = $.extend({}, defaultParams, params);
		var ajaxParams = {
			url: params.baseURL + "/get_entities/",
			dataType: "json",
			success: function(data) {
/*				console.log("DATA:", data);
				console.log("MAPPED DATA:", $.map(data, function(obj, i) {
					return deserializeObject(obj);
				}));*/
				if (params.success) {
					params.success($.map(data, function(obj, i) {
						return deserializeObject(obj);
					}));
				}
			},
			error: function() {
				if (params.error) {
					params.error();
				}
			},
			data: {
				model: params.model,
				filters: JSON.stringify(params.filters),
				order: params.order,
				limit: params.limit,
				offset: params.offset
			},
			type: "GET"
		}
		$.ajax(ajaxParams);
	}

	function gaePostRequest(params) {
		var defaultParams = {
			baseURL: null,
			model: null,
			object: null,
			operation: "",
			success: null, 
			error: null
		};
		var operationMap = {
			"put":    { url: "/put_entity/", serialize: serializeObject }, 
			"delete": { url: "/delete_entity/", serialize: serializeObjectKey }
		};
		var params = $.extend({}, defaultParams, params);
		var ajaxParams = {
			url: params.baseURL + operationMap[params.operation].url, 
			dataType: "json",
			success: function(data) {
				//console.log(params.success);
				/* Post-process the object to add the key */
				params.object.key(new GaeKey(data.__key.__kind, data.__key.__name, data.__key.__repr));
				params.object.__is_saved = data.__is_saved;
				if (params.success) {
					params.success(params.object);
				}
			},
			error: function() {
				if (params.error) {
					params.error();
				}
			},
			data: {
				model: params.model,
				object: operationMap[params.operation].serialize(params.object)
			},
			type: "POST"
		}
		$.ajax(ajaxParams);
	}

	function GaeKey(kind, name, repr) {
		this.__kind = kind;
		this.__name = name;
		this.__repr = repr;
		return this;
	}
	GaeKey.prototype = {
		kind: function() {
			return this.__kind;
		},
		name: function() {
			return this.__name;
		},
		toString: function() {
			return this.__repr;
		}
	};


	function GaeEntity(entityType, keyName) {
		this.__key = new GaeKey(entityType, keyName)
		return this;
	}
	GaeEntity.prototype = {
		put: function(callback) {
			/* Make a POST to appengine */
			gaePostRequest( {
				baseURL: gaeBaseURL,
				model: this.key().kind(),
				object: this,
				operation: "put",
				success: callback ? callback.success() : null, 
				error: callback ? callback.error() : null
			});
		}, 
		remove: function(callback) {
			/* Make a POST to appengine */
			gaePostRequest( {
				baseURL: gaeBaseURL,
				model: this.key().kind(),
				object: this,
				operation: "delete",
				success: callback ? callback.success() : null, 
				error: callback ? callback.error() : null
			});
		}, 
		key: function(newKey) {
			if (newKey == undefined) {
				return this.__key;
			}
			else {
				/* Do not allow the user to set the key of a saved object 
				 * OR if he/she does that, we will save this as a new entity.
				 */
				if (typeof(newKey) == "string") {
					this.__key = new GaeKey(this.key().kind(), null, newKey);
				}
				else {
					this.__key = newKey;
				}
				return this;
			}
		},
		is_saved: function() {
			if (this.__is_saved == undefined) {
				return false;
			}
			else {
				return this.__is_saved;
			}
		}
	};

	function GaeQuery(entityType) {
		//console.log(this);
		this.__entityType = entityType;
		this._filters = [];
		this._order = "";
		return this;
	}
	GaeQuery.prototype = $.extend({}, new Array(), {
		clone: function() {
			var that = new GaeQuery(this.__entityType);
			that._filters = this._filters;
			that._order = this._order;
			return that;
		},
		filter: function(attr, value) {
			var that = this.clone();
			that._filters.push([attr, value]);
			return that;
		},
		order: function(by) {
			var that = this.clone();
			that._order = by;
			return that;
		},
		get: function(callback) {
			/* Make a GET to appengine */
			return this.fetch(1, 0, callback);
		},
		fetch: function(limit, offset, callback) {
			/* Make a GET to appengine */
			gaeGetRequest( {
				baseURL: gaeBaseURL,
				model: this.__entityType,
				filters: this._filters,
				order: this._order,
				limit: limit,
				offset: offset,
				success: callback ? callback.success() : null, 
				error: callback ? callback.error() : null
			});
			return this;
		}, 
		count: function(proc) {
			/* Make a GET to appengine */
		},
		create: function() {
			return new GaeEntity(this.__entityType);
		}
	});

	gaeBaseURL = "";
	gaeQuery = function(entityType) {
		return new GaeQuery(entityType);
	}
	gaeQuery.set_base_url = function(baseURL) {
		gaeBaseURL = baseURL;
	};
	//console.log(gaeQuery);
	window.gaeQuery = window.$gae = gaeQuery;
}(window));

function GaeDeferred() {
	this._successHandlers = [];
	this._errorHandlers = [];
	return this;
}
GaeDeferred.prototype = {
	success: function() {
		var _successHandlers = this._successHandlers;
		return (function(data) {
			$(_successHandlers).each(function(i) {
				this(data);
			});
		});
	},
	error: function() {
		var _errorHandlers = this._errorHandlers;
		return (function() {
			$(_errorHandlers).each(function(i) {
				this();
			});
		});
	},
	addCallBack: function(proc) {
		this._successHandlers.push(proc);
		return this;
	},
	addErrBack: function(proc) {
		this._errorHandlers.push(proc);
		return this;
	}
};
